package com.mofang.ticketing.user.serve.role.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.mofang.ticketing.common.core.dto.PrimaryIdDTO;
import com.mofang.ticketing.common.core.enums.account.BelongEnum;
import com.mofang.ticketing.common.core.enums.account.RoleTypeEnum;
import com.mofang.ticketing.common.core.enums.base.SysEnableEnum;
import com.mofang.ticketing.common.core.exception.BizException;
import com.mofang.ticketing.common.redis.constant.RedisConstant;
import com.mofang.ticketing.common.redis.service.RedisService;
import com.mofang.ticketing.common.security.util.SecurityUtil;
import com.mofang.ticketing.user.mapping.entity.*;
import com.mofang.ticketing.user.mapping.mapper.MerchantInfoDOMapper;
import com.mofang.ticketing.user.mapping.mapper.SysPermissionDOMapper;
import com.mofang.ticketing.user.mapping.mapper.SysRoleDOMapper;
import com.mofang.ticketing.user.mapping.mapper.SysRolePermissionDOMapper;
import com.mofang.ticketing.user.serve.account.bo.Actions;
import com.mofang.ticketing.user.serve.account.bo.Children;
import com.mofang.ticketing.user.serve.account.bo.Permission;
import com.mofang.ticketing.user.serve.role.dto.request.RoleAddReqDTO;
import com.mofang.ticketing.user.serve.role.dto.request.RoleListPageReqDTO;
import com.mofang.ticketing.user.serve.role.dto.request.RoleUpdateReqDTO;
import com.mofang.ticketing.user.serve.role.dto.response.RoleDetailRespDTO;
import com.mofang.ticketing.user.serve.role.dto.response.RoleListPageRespDTO;
import com.mofang.ticketing.user.serve.role.dto.response.MerchantRoleSelectRespDTO;
import com.mofang.ticketing.user.serve.role.mapper.RoleMapper;
import com.mofang.ticketing.user.serve.role.service.RoleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

/**
 * @author LGC
 * @date 2023/3/15 14:40
 */
@Slf4j
@Service
public class RoleServiceImpl implements RoleService {
    @Resource
    private RedisService redisService;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private SysRoleDOMapper sysRoleDOMapper;
    @Resource
    private SysPermissionDOMapper sysPermissionDOMapper;
    @Resource
    private SysRolePermissionDOMapper sysRolePermissionDOMapper;
    @Resource
    private MerchantInfoDOMapper merchantInfoDOMapper;

    @Override
    public RoleDetailRespDTO roleDetail(PrimaryIdDTO dto) {
        return roleMapper.roleDetail(dto.getId());
    }

    @Override
    public IPage<RoleListPageRespDTO> roleListPage(RoleListPageReqDTO dto) {
        return roleMapper.roleListPage(dto.getPage().getPage(), dto);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void roleAdd(RoleAddReqDTO dto) {
        // 角色名称不能重复
        SysRoleDO sysRoleDO = getRoleByRoleName(dto.getRoleName());
        if (sysRoleDO != null) {
            throw new BizException("角色名称已存在");
        }
        MerchantInfoDO merchantInfoDO = merchantInfoDOMapper.selectById(dto.getMerchantId());
        if (merchantInfoDO == null) {
            throw new BizException("商户不存在");
        }
        // 插入角色
        SysRoleDO insertRole = insertRole(dto);
        // 插入角色权限
        insertRolePermission(dto.getAuthority(), insertRole.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void roleUpdate(RoleUpdateReqDTO dto) {
        SysRoleDO sysRoleDO = sysRoleDOMapper.selectById(dto.getId());
        if (sysRoleDO == null) {
            throw new BizException("角色不存在");
        }
        // 角色名称不能重复
        SysRoleDO roleDO = getRoleByRoleName(dto.getRoleName());
        if (roleDO != null && !sysRoleDO.getId().equals(roleDO.getId())) {
            throw new BizException("角色名称已存在");
        }
        if (sysRoleDO.getRoleType().equals(RoleTypeEnum.MERCHANT_MEMBER.getKey())) {
            if (StrUtil.isBlank(dto.getRoleName()) || dto.getMerchantId() == null) {
                throw new BizException("修改商户成员角色参数缺失");
            }
            MerchantInfoDO merchantInfoDO = merchantInfoDOMapper.selectById(dto.getMerchantId());
            if (merchantInfoDO == null) {
                throw new BizException("商户不存在");
            }
        }
        Long roleId = sysRoleDO.getId();
        String authority = dto.getAuthority();
        // 更新角色
        new LambdaUpdateChainWrapper<>(sysRoleDOMapper)
                .set(StrUtil.isNotBlank(dto.getRoleName()), SysRoleDO::getRoleName, dto.getRoleName())
                .set(dto.getMerchantId() != null && dto.getMerchantId() > 0, SysRoleDO::getRoleName, dto.getRoleName())
                .set(SysRoleDO::getAuthority, authority)
                .set(StrUtil.isBlank(sysRoleDO.getRoleCode()), SysRoleDO::getRoleCode, buildRoleCode(dto.getRoleName(), null))
                .set(SysRoleDO::getUpdateUserId, SecurityUtil.getUserId())
                .set(SysRoleDO::getUpdateTime, new Date())
                .set(SysRoleDO::getRevision, sysRoleDO.getRevision() + 1)
                .eq(SysRoleDO::getRevision, sysRoleDO.getRevision())
                .eq(SysRoleDO::getId, roleId).update();
        // 删除原角色权限
        sysRolePermissionDOMapper.delete(new LambdaQueryWrapper<SysRolePermissionDO>().eq(SysRolePermissionDO::getRoleId, roleId));
        // 插入新的角色权限
        insertRolePermission(authority, roleId);

        List<SysUserDO> userDOList = roleMapper.selectUserListByRoleId(roleId);
        for (SysUserDO sysUserDO : userDOList) {
            redisService.del(String.format(RedisConstant.LOGIN_INFO_KEY, sysUserDO.getAccount()));
        }
    }

    @Override
    public List<MerchantRoleSelectRespDTO> merchantRoleSelect(Long merchantId) {
        return roleMapper.merchantRoleSelect(merchantId);
    }

    /**
     * 构建角色code
     *
     * @param roleName
     * @param roleCode
     * @return
     */
    private String buildRoleCode(String roleName, String roleCode) {
        String rs = "";
        if (StrUtil.isBlank(roleCode)) {
            Long count = new LambdaQueryChainWrapper<>(sysRoleDOMapper)
                    .eq(SysRoleDO::getRoleName, roleName)
                    .count();
            rs = PinyinUtil.getPinyin(roleName, "_").toLowerCase(Locale.ROOT);
            if (count > 0) {
                count++;
                rs = (PinyinUtil.getPinyin(roleName, "_") + "_" + count).toLowerCase(Locale.ROOT);
            }
        } else {
            rs = roleCode;
        }
        return rs;
    }

    /**
     * 插入角色权限
     *
     * @param authority
     * @param roleId
     */
    private void insertRolePermission(String authority, Long roleId) {
        // 权限json解析
        if (StringUtils.isNotBlank(authority)) {
            List<String> perKeyList = getPerKeyByAuthority(authority);
            List<SysPermissionDO> permissionDOList = new LambdaQueryChainWrapper<>(sysPermissionDOMapper)
                    .in(SysPermissionDO::getPerKey, perKeyList).list();
            if (CollUtil.isNotEmpty(permissionDOList)) {
                List<SysRolePermissionDO> insertRolePermissionList = permissionDOList.stream().map(p ->
                        new SysRolePermissionDO(roleId, p.getId())).collect(Collectors.toList());
                roleMapper.insertRolePermissionBatch(insertRolePermissionList);
            }
        }
    }

    /**
     * 获取权限key
     *
     * @param authority
     * @return
     */
    private List<String> getPerKeyByAuthority(String authority) {
        List<String> perKeys = new ArrayList<>();
        List<Permission> permissions = JSONUtil.toList(authority, Permission.class);
        for (Permission permission : permissions) {
            List<Children> children = permission.getChildren();
            if (CollUtil.isNotEmpty(children) && children.size() > 0) {
                for (Children child : children) {
                    perKeys.add(child.getPermissionId());
                    List<Actions> actions = child.getActions();
                    if (CollUtil.isNotEmpty(actions) && children.size() > 0) {
                        for (Actions action : actions) {
                            if (action.isChecked()) {
                                perKeys.add(action.getPermissionId());
                            }
                        }
                    }
                }
            }
        }
        return perKeys;
    }

    private SysRoleDO insertRole(RoleAddReqDTO dto) {
        Long userId = SecurityUtil.getUserId();
        String roleCode = buildRoleCode(dto.getRoleName(), null);
        SysRoleDO insertRole = new SysRoleDO();
        insertRole.setBelong(BelongEnum.MERCHANT.getKey());
        insertRole.setRoleType(RoleTypeEnum.MERCHANT_MEMBER.getKey());
        insertRole.setMerchantId(dto.getMerchantId());
        insertRole.setRoleName(dto.getRoleName());
        insertRole.setRoleCode(roleCode);
        insertRole.setAuthority(dto.getAuthority());
        insertRole.setIsEnable(SysEnableEnum.ENABLE.getKey());
        insertRole.setCreateUserId(userId);
        insertRole.setCreateTime(new Date());
        insertRole.setUpdateUserId(userId);
        insertRole.setUpdateTime(new Date());
        sysRoleDOMapper.insert(insertRole);
        return insertRole;
    }

    /**
     * 根据名称获取角色
     */
    private SysRoleDO getRoleByRoleName(String roleName) {
        return new LambdaQueryChainWrapper<>(sysRoleDOMapper)
                .eq(SysRoleDO::getRoleName, roleName).one();
    }
}
